package cn.t.facade.user;


import cn.dev33.satoken.exception.NotWebContextException;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import cn.t.constants.SaTokenProperties;
import cn.t.enums.user.RoleTypeEnum;
import cn.t.enums.user.ScenarioCodeEnum;
import cn.t.model.user.ParticipantRoleDO;
import cn.t.model.user.RoleDeviceDO;
import cn.t.service.user.ParticipantRoleService;
import cn.t.service.user.RoleDeviceService;
import cn.t.user.converter.ParticipantVOConverter;
import cn.t.user.converter.UserQueryVOConverter;
import cn.t.vo.user.FirmVO;
import cn.t.vo.user.MySaTokenInfoVO;
import cn.t.vo.user.ParticipantVO;
import cn.t.vo.user.ParticipantWithUserVO;
import cn.t.vo.user.RoleVO;
import cn.t.vo.user.UserSessionVO;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static cn.t.constants.Constants.REDIS_KEY__LOGIN_SESSION;


/**
 * @author l
 */
@Slf4j
@Component
public class UserSessionFacade {

    public String operatorInfo() {
        UserSessionVO session = getSession();
        if (session == null) {
            return "system";
        }

        return session.operatorInfo();
    }

    public UserSessionVO getSession() {
        try {

            String token = StpUtil.getTokenValue();
            String redisKey = String.format(REDIS_KEY__LOGIN_SESSION, token);
            boolean existed = redissonClient.getBucket(redisKey).touch();

            if (!existed) {
                return null;
            }

            UserSessionVO userSessionVO = (UserSessionVO) redissonClient.getBucket(redisKey).get();
            redissonClient.getBucket(redisKey).set(userSessionVO, saTokenProperties.getTimeout(), TimeUnit.SECONDS);

            return userSessionVO;

        } catch (NotWebContextException e) {
            log.warn(e.getMessage());
            return null;
        } catch (Exception e) {
            log.warn(e.getMessage(), e);
            return null;
        }
    }


    public List<RoleVO> getRoleList(UserSessionVO session, Long firmId) {
        List<FirmVO> firms = session.getFirms();
        if (CollectionUtils.isEmpty(firms)){
            return new ArrayList<>();
        }
        if (firmId!=null){
            FirmVO firmVO = firms.stream().filter(f -> firmId.equals(f.getCustomerId())).findFirst().orElse(null);
            return getRoleVOS(firmVO);
        }
        return firms.stream().flatMap(firmVO -> getRoleVOS(firmVO).stream()).collect(Collectors.toList());
    }

    private static List<RoleVO> getRoleVOS(FirmVO firmVO) {

        if (firmVO == null || CollectionUtils.isEmpty(firmVO.getParticipants())) {
            return new ArrayList<>();
        }
        ParticipantVO participantVO = firmVO.getParticipants().stream().findFirst().orElse(null);
        if (participantVO==null || CollectionUtils.isEmpty(participantVO.getRoles())){
            return new ArrayList<>();
        }
        return participantVO.getRoles();
    }

    public Set<Long> getDeviceIdList(UserSessionVO session, Long firmId, RoleTypeEnum roleTypeEnum) {
        List<RoleVO> roleList = getRoleList(session, firmId);
        Set<Long>    roleIds=  roleList.stream().filter(roleVO -> roleTypeEnum.getCode().equals(roleVO.getRoleType())).map(RoleVO::getRoleId).collect(Collectors.toSet());
            if (CollectionUtils.isEmpty(roleList)){
                return new HashSet<>();
            }
        List<RoleDeviceDO> tmp = this.roleDeviceService.findByRoleId(roleIds);
        if (CollectionUtils.isEmpty(tmp)) {
            return new HashSet<>();
        }

        return tmp.stream().map(RoleDeviceDO::getDeviceId).collect(Collectors.toSet());
    }

    public Set<Long> getReplenishDeviceIds(Long firmId) {

        UserSessionVO userSession = getSession();
        if (userSession == null || userSession.getFirms() == null || userSession.getFirms().isEmpty()) {
            return null;
        }
        Set<Long> roleIds = new HashSet<>();

        for (FirmVO firmVO : userSession.getFirms()) {

            if (CollectionUtils.isEmpty(firmVO.getParticipants())) {
                continue;
            }

                if (firmId!=null && !firmId.equals(firmVO.getCustomerId())) {
                    continue;
                }

            for (ParticipantVO participantVO : firmVO.getParticipants()) {
                List<ParticipantRoleDO> replenishRoleList = participantRoleService.findMetalReplenishByParticipantId(participantVO.getParticipantId());

                List<ParticipantRoleDO> combined = new ArrayList<>();
                if (!CollectionUtils.isEmpty(replenishRoleList)) {
                    combined.addAll(replenishRoleList);
                }

                for (ParticipantRoleDO participantRoleDO : combined) {
                    roleIds.add(participantRoleDO.getRoleId());
                }
            }
        }

        List<RoleDeviceDO> tmp = this.roleDeviceService.findByRoleId(roleIds);
        if (CollectionUtils.isEmpty(tmp)) {
            return null;
        }

        return tmp.stream().map(RoleDeviceDO::getDeviceId).collect(Collectors.toSet());
    }
    public ParticipantWithUserVO getParticipantWithUserVO(Long firmId, ScenarioCodeEnum scenarioCode) {

        UserSessionVO userSession = getSession();
        if (userSession == null || userSession.getFirms() == null || userSession.getFirms().isEmpty()) {
            return null;
        }

        FirmVO firmVO = userSession.getFirms().stream().filter(f -> firmId.equals(f.getCustomerId())).findFirst().orElse(null);
        if (firmVO == null || CollectionUtils.isEmpty(firmVO.getParticipants())) {
            return null;
        }

        for (ParticipantVO participant : firmVO.getParticipants()) {
            if (scenarioCode.getCode().equals(participant.getScenarioId())) {
                ParticipantWithUserVO ret = participantVOConverter.toParticipantWithUserVO(participant);
                ret.setFirmCode(firmVO.getFirmCode());
                ret.setFirmId(firmVO.getCustomerId());
                ret.setFirmName(firmVO.getFirmName());
                ret.setUserId(userSession.getUserId());
                return ret;
            }
        }

        return null;

    }

    public void login(UserSessionVO userSessionVO) {
        // 登录鉴权
        String loginId = String.format("%d-%d-%d", userSessionVO.getPassportId(), userSessionVO.getUserId(), userSessionVO.getScenarioId());

        SaLoginModel loginModel = new SaLoginModel();
        if (userSessionVO.getDeceiveId() != null) {
            loginModel.setDevice(String.valueOf(userSessionVO.getDeceiveId()));
        }
        loginModel.setTimeout(saTokenProperties.getTimeout());

        StpUtil.login(loginId, loginModel);

        userSessionVO.setMySaTokenInfoVO(new MySaTokenInfoVO(StpUtil.getTokenInfo()));
        userSessionVO.setToken(userSessionVO.getMySaTokenInfoVO().getTokenValue());

        String redisKey = String.format(REDIS_KEY__LOGIN_SESSION, userSessionVO.getMySaTokenInfoVO().getTokenValue());
        boolean isExists = redissonClient.getBucket(redisKey).touch();
        if (isExists) {
            log.warn("redis key {} is exists. passportId={} userId={} Participant={}", redisKey, userSessionVO.getPassportId(), userSessionVO.getUserId(), userSessionVO.getFirms());
        }
        redissonClient.getBucket(redisKey).set(userSessionVO, saTokenProperties.getTimeout(), TimeUnit.SECONDS);
        cacheRights(userSessionVO);
    }

    @Async
    public void cacheRights(UserSessionVO userSession) {
        if (userSession.getFirms() == null || userSession.getFirms().isEmpty()) {
            return;
        }
        resourceInfoFacade.clearMenuTree(userSession.getUserId());
        resourceInfoFacade.getMenuTreeByUser(userSession.getUserId(), getParticipantIds(userSession));
    }

    public List<Long> getParticipantIds(UserSessionVO userSession) {
        if (userSession == null) {
            userSession = getSession();
        }

        List<Long> participantIds = new ArrayList<>();

        if (userSession.getFirms() == null || userSession.getFirms().isEmpty()) {
            return null;
        }
        for (FirmVO firmVO : userSession.getFirms()) {
            if (CollectionUtils.isEmpty(firmVO.getParticipants())) {
                continue;
            }

            for (ParticipantVO participant : firmVO.getParticipants()) {
                participantIds.add(participant.getParticipantId());
            }
        }

        return participantIds;
    }

    public void removeSession() {
        String token = StpUtil.getTokenValue();
        String redisKey = String.format(REDIS_KEY__LOGIN_SESSION, token);
        boolean success = redissonClient.getBucket(redisKey).delete();
        if (!success) {
            log.warn("remove session fault. token={}", token);
        }

        StpUtil.logout();
    }

    @Autowired
    private RedissonClient redissonClient;


    @Autowired
    private ResourceInfoFacade resourceInfoFacade;

    @Autowired
    private RoleDeviceService roleDeviceService;

    @Autowired
    private ParticipantRoleService participantRoleService;

    @Autowired
    private SaTokenProperties saTokenProperties;

    @Autowired
    private ParticipantVOConverter participantVOConverter;

    @Autowired
    private UserQueryVOConverter userQueryVOConverter;

}
